/**************************************************************/
/*  This file is licensed from, and is a trade secret of:     */
/*                                                            */
/*    Mercury Computer Systems, Incorporated                  */
/*    199 Riverneck Road                                      */
/*    Chelmsford, Massachusetts 01824-2820                    */
/*    United States of America                                */
/*    Telephone + 1 978 256-1300                              */
/*                                                            */
/*  Refer to your Software License Agreement for provisions   */
/*  on use, duplication, or disclosure.                       */
/*  US. Government users and prime contractors are also       */
/*  governed by the following Data Rights Notice:             */
/*  Use, duplication or disclosure by the US. Government is   */
/*  subject to the provisions set forth in FAR 52.227-14 for  */
/*  restricted rights or other agency data rights provisions  */
/*  on commercial software and documentation, as applicable.  */
/*                                                            */
/*  This file: (a) was developed at private expense, and no   */
/*  part was developed with government funds, (b) is a        */
/*  trade secret of Mercury Computer Systems, Inc. for the    */
/*  purposes of the Freedom of Information Act, (c) is        */
/*  "commercial computer software" subject to limited         */
/*  utilization as provided in the above noted Software       */
/*  License Agreement and (d) is in all respects data         */
/*  belonging exclusively to Mercury Computer Systems, Inc..  */
/*                                                            */
/**************************************************************/

/******************************************************************/
/* Copyright (c) 1999-2003, Mercury Computer Systems, Inc.,       */
/* Chelmsford MA.,and all third party embedded software sources.  */
/* All rights reserved under the Copyright laws of US. and        */
/* international treaties.                                        */
/*                                                                */
/* Patent Pending                                                 */
/******************************************************************/


//$COPYRIGHT_END


#include <cstdlib>
#include <cstdio>
#include <string>
#include "RsBlock.h"
#include "SceOsMisc.h"

using namespace std;

extern "C" {
  RSBLOCK_EXPORT
  POA_CF::Resource *
  CreateRsBlockEncoder (const char * identifier)
  {
    RsBlockEncoder * encoder;

    if (RsBlockEncoder::Create (encoder, identifier) != success) {
      return 0;
    }

    return (POA_CF::Resource *) encoder;
  }
}

status
RsBlockEncoder::Create (RsBlockEncoder *& handle,
                        const char * identifier)
{
  RsBlockEncoder * Temp = new RsBlockEncoder (identifier);

  if (!Temp) {
    return fail;
  }

  if (!Temp->Init ()) {
    handle = 0;
    return fail;
  }

  handle = Temp;
  return success;
}

RsBlockEncoder::RsBlockEncoder (const char * identifier)
  : CF__Resource_impl (identifier),
    m_threadrunning (false),
    m_bStop (false)
{
  unsigned int input_buffer_size;
  unsigned int output_buffer_size;

  // Initialize buffers.
  input_buffer_size = 512;
  m_input = new char [input_buffer_size];
  output_buffer_size = 105;
  m_output = new char [output_buffer_size];

  // Define ports.
  m_rsblockout = new RsBlockOutPort_impl (m_output, output_buffer_size);
  m_rsblockin = new RsBlockInPort_impl (input_buffer_size);
}

RsBlockEncoder::~RsBlockEncoder ()
{
  delete m_input;
  delete m_rsblockin;
  delete m_output;
  delete m_rsblockout;
  delete m_gf;
  delete m_genpoly;
  delete m_encMsg;
}

status
RsBlockEncoder::Init ()
{
#ifdef MCS_VERBOSE
  std::cout << "RsBlockEncoder: Initializing." << std::endl;
#endif

  unsigned int block_prim_poly[8] = {
    1, 0, 0, 1, 0, 0, 0, 1
  };
  unsigned int block_gen_poly[34] = {   
    124, 125, 33, 74, 44, 9, 14, 20, 127, 46, 126, 86, 70, 46, 73, 70, 
    22, 68, 72, 21, 17, 48, 63, 44, 23, 20, 38, 2, 17, 119, 30, 121, 113, 1   
  };

  // Initialize the parameters.
  m_n = 105;
  m_k = 72;
  m_N = 127;
  m_K = 94;
  m_m = 7;
  m_b = 1;

  m_gf = new galois::GaloisField (m_m, block_prim_poly);

  galois::GaloisFieldElement gfe_block_genpoly[34] = {
    galois::GaloisFieldElement (m_gf, block_gen_poly[0]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[1]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[2]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[3]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[4]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[5]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[6]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[7]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[8]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[9]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[10]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[11]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[12]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[13]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[14]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[15]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[16]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[17]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[18]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[19]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[20]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[21]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[22]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[23]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[24]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[25]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[26]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[27]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[28]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[29]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[30]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[31]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[32]),
    galois::GaloisFieldElement (m_gf, block_gen_poly[33]),
  };

  m_genpoly = new galois::GaloisFieldPolynomial (m_gf, 33, gfe_block_genpoly);

  if ((m_n-m_k)%2==0) {
    m_t = (m_n - m_k) / 2;
  }
  else {
    m_t = (m_n - m_k - 1) / 2;
  }

  m_encMsg = new galois::GaloisFieldPolynomial (m_gf, m_N-1);
  return success;
}

void
RsBlockEncoder::RunWorker (void * param)
{
  RsBlockEncoder * component = (RsBlockEncoder *) param;

#ifdef MCS_VERBOSE
  std::cout << "RsBlockEncoder: Component is running." << std::endl;
#endif

  component->RunLoop();

#ifdef MCS_VERBOSE
  std::cout << "RsBlockEncoder: Component is stopping." << std::endl;
#endif
}

status
RsBlockEncoder::RunLoop ()
{
  m_bStop = false;

  while (!m_bStop) {
    Run ();
  }

  m_threadrunning = false;
  m_thread.detach ();
  return success;
}

void
RsBlockEncoder::start ()
  throw (CORBA::SystemException, CF::Resource::StartError)
{
  if (m_threadrunning) {
    return;
  }

  m_threadrunning = true;
  m_thread.start (RunWorker, (void *) this);
  CF__Resource_impl::start ();
}

void
RsBlockEncoder::stop ()
  throw (CORBA::SystemException, CF::Resource::StopError)
{
  m_bStop = true;
}

CORBA::Object_ptr
RsBlockEncoder::getPort (const CORBA::Char * name)
  throw (CORBA::SystemException, CF::PortSupplier::UnknownPort)
{
  string temp (name);

  if (temp == "RsBlockEncoderOut") {
    return m_rsblockout->getPort ();
  }
  else if (temp == "RsBlockEncoderIn") {
    return m_rsblockin->getPort ();
  }
  else {
    throw CF::PortSupplier::UnknownPort ();
  }
}

status
RsBlockEncoder::Run ()
{
  unsigned int i;
  unsigned int m_input_count = m_rsblockin->GetData (&m_input);

  if (m_input_count == 0) {
    return fail;
  }
  else if (m_input_count < m_k) {
    // Not enough data in buffer; wait for more.
    m_rsblockin->ReleaseDataBuffer (0);
    return success;
  }

#ifdef MCS_VERBOSE
  std::cout << "RsBlockEncoder: Received " << m_input_count
	    << " samples." << std::endl;
#endif

  m_samples_read = 0;
  m_output_count = 0;

  while (m_input_count >= m_k) {
    // Shift the polynomial n-k units (for the parities).
    for (i = 0; i < (m_n - m_k) ; i++) {
      (*m_encMsg)[i] = 0;
    }

    // Fill in the sequence to be encoded.
    for (i = m_n-m_k; i < m_n; i++) {
      (*m_encMsg)[i] = m_input[m_samples_read + i-(m_n-m_k)];
    }

    // Zero-pad the beginning.
    for (i = m_n; i < m_N-1; i++) {
      (*m_encMsg)[i] = 0;
    }

    galois::GaloisFieldPolynomial blockParities = *m_encMsg % *m_genpoly;
    galois::GaloisFieldPolynomial blockEncoderOut = *m_encMsg + blockParities; 

    for (i=0; i < m_n; i++) {
      m_output[i] = (char) blockEncoderOut[i].poly();
    }

    m_samples_read += m_k;
    m_input_count -= m_k;
    m_output_count += m_n;

#ifdef MCS_VERBOSE
  std::cout << "RsBlockEncoder: Sending " << m_n << " samples." << std::endl;
#endif

    m_rsblockout->pushData (m_output, m_n);
  } // while (m_input_count >= m_k)

  m_rsblockin->ReleaseDataBuffer (m_samples_read);
  return success;
} // End of RsBlockEncode::Run()

